﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Net.Sockets;
using System.Net;
using psafth.RCTiming.Common;

namespace psafth.RCTiming.ScoreBoard
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private DispatcherTimer _dispatcherTimer;
        private BackgroundWorker _bg;

        private DateTime _stopTime;
        private bool _runTimer;

        public ObservableCollectionExt<MCDriver> Competitors
        {
            get;
            set;
        }

        public MainWindow()
        {
            InitializeComponent();

            _dispatcherTimer = new DispatcherTimer();
            _dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 10);
            _dispatcherTimer.Tick += _dispatcherTimer_Tick;
            _dispatcherTimer.Start();

            RaceGrid.DataContext = Competitors;

            this.tbMiddleCenter.Visibility = System.Windows.Visibility.Visible;
            this.tbHeader.Visibility = System.Windows.Visibility.Collapsed;
            this.tbTimer.Visibility = System.Windows.Visibility.Collapsed;
            RaceGrid.Visibility = System.Windows.Visibility.Collapsed;

            _bg = new BackgroundWorker();
            _bg.WorkerReportsProgress = true;
            _bg.ProgressChanged += bg_ProgressChanged;
            _bg.DoWork += bg_DoWork;
            _bg.RunWorkerAsync();
        }

        private void bg_DoWork(object sender, DoWorkEventArgs e)
        {
            UdpClient client = new UdpClient();

            client.ExclusiveAddressUse = false;
            IPEndPoint localEp = new IPEndPoint(IPAddress.Any, 2222);

            client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            client.ExclusiveAddressUse = false;

            client.Client.Bind(localEp);

            IPAddress multicastaddress = IPAddress.Parse("239.0.0.222");
            client.JoinMulticastGroup(multicastaddress, 50);

            while (true)
            {
                byte[] data = client.Receive(ref localEp);

                _bg.ReportProgress(1, MulticastHelper.ByteArrayToObject(data));
            }
        }

        private void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.UserState != null)
            {
                if (e.UserState.GetType().Equals(typeof(HeatCommand)))
                {
                    MCHeat heat = ((HeatCommand)e.UserState).Heat;
                    List<MCDriver> competitors = ((HeatCommand)e.UserState).Competitors;

                    if (competitors == null)
                        competitors = heat.Competitors;

                    if (competitors != null)
                        Competitors = competitors.ToObservableCollectionExt();

                    _runTimer = heat.IsRunning;

                    if (heat.TimeElapsed < new TimeSpan(0, heat.Duration, 0))
                    {
                        //this._stopTime = DateTime.Now.Add(heat.TimeElapsed);

                        if (heat.TimeElapsed.TotalSeconds == 0)
                        {
                            tbTimer.Text = string.Format("{0}:00", heat.Duration.ToString("00"));
                        }
                        else
                        {
                            UpdateTimer();
                        }
                    }

                    this.tbHeader.Text = heat.Title;

                    this.tbMiddleCenter.Visibility = System.Windows.Visibility.Collapsed;
                    this.tbHeader.Visibility = System.Windows.Visibility.Visible;
                    this.tbTimer.Visibility = System.Windows.Visibility.Visible;
                    this.RaceGrid.Visibility = System.Windows.Visibility.Visible;

                    UpdateDGW();
                }

                if (e.UserState.GetType().Equals(typeof(List<MCDriver>)))
                {
                    Competitors = ((List<MCDriver>)e.UserState).ToObservableCollectionExt();

                    Competitors.OrderBy(c => c.Position);

                    _runTimer = true;

                    UpdateDGW();
                }

                if (e.UserState.GetType().Equals(typeof(StartCommand)))
                {
                    StartCommand s = (StartCommand)e.UserState;
                    this._stopTime = DateTime.Now.Add(s.HeatDurationLeft);
                    _runTimer = true;
                }

                if (e.UserState.GetType().Equals(typeof(PauseCommand)))
                {
                    PauseCommand p = (PauseCommand)e.UserState;

                    _runTimer = false;

                    UpdateTimer();
                }

                if (e.UserState.GetType().Equals(typeof(StopCommand)))
                {
                    StopCommand p = (StopCommand)e.UserState;
                    _runTimer = false;
                }
            }
            else
            {
                this.tbTimer.Foreground = Brushes.YellowGreen;
                this.tbHeader.Text = string.Empty;
                this.tbTimer.Text = string.Empty;
                _runTimer = false;

                this.tbMiddleCenter.Visibility = System.Windows.Visibility.Visible;
                this.tbHeader.Visibility = System.Windows.Visibility.Collapsed;
                this.tbTimer.Visibility = System.Windows.Visibility.Collapsed;
                this.RaceGrid.Visibility = System.Windows.Visibility.Collapsed;

                Competitors = new ObservableCollectionExt<MCDriver>();

                UpdateDGW();
            }
        }

        private void UpdateTimer()
        {
            TimeSpan s = _stopTime.Subtract(DateTime.Now);

            tbTimer.Text = s.ToString(@"mm\:ss");

            if (s.TotalSeconds < 0)
            {
                tbTimer.Foreground = Brushes.Red;
            }
            else
            {
                tbTimer.Foreground = Brushes.YellowGreen;
            }
        }

        private void UpdateDGW()
        {
            RaceGrid.DataContext = Competitors;
            //OnPropertyChanged("Competitors");
        }

        void _dispatcherTimer_Tick(object sender, EventArgs e)
        {
            if (_runTimer)
            {
                UpdateTimer();
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }

    public static class CollectionUtils
    {
        public static ObservableCollectionExt<T> ToObservableCollectionExt<T>(this IEnumerable<T> thisCollection)
        {
            if (thisCollection == null) return null;
            var oc = new ObservableCollectionExt<T>();

            foreach (var item in thisCollection)
            {
                oc.Add(item);
            }

            return oc;
        }
    }
}